package com.upeoe.lucrocrawler.crawler;

import com.upeoe.lucrocrawler.beans.Table;
import com.upeoe.lucrocrawler.dao.ILucroDao;
import com.upeoe.lucrocrawler.dao.LucroDaoImpl;
import com.upeoe.lucrocrawler.utils.DateKit;
import com.upeoe.lucrocrawler.utils.PropsKit;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ven_hxz
 * @create 2016-05-22 下午5:09
 * 爬取Lucro并保存数据到数据库
 */
public class Crawler {
    private static final String FILE_PATH = PropsKit.getValue("filepath");              //缓存文本路径
    private static final String LOGIN_URL = PropsKit.getValue("login_url");             //登录页面
    private static final String FORM_URL = PropsKit.getValue("form_url");               //数据页面

    private static final String AMEMBER_LOGIN = PropsKit.getValue("amember_login");     //用户名
    private static final String AMEMBER_PASS = PropsKit.getValue("amember_pass");       //密码
    private static final String SUB_ACCOUNTS = PropsKit.getValue("sub_accounts");       //账号
    private static final String LEVELS = PropsKit.getValue("levels");                   //筛选等级
    private static final String SELNUMROWS = PropsKit.getValue("selnumrows");           //单次查询条数

    private static final String REGEX_PATTERN = "<td align.+?>(.+?)</td>";              //正则匹配

    private static Logger logger = LoggerFactory.getLogger(CrawlerV1.class);

    /**
     * 执行爬虫事件,爬取数据并保存到数据库
     */
    public static void me(String dateFrom, String dateTo) {

        System.out.println("====================");

        System.out.println(Calendar.getInstance().getTime().toLocaleString() + "开始执行任务");
        boolean crawlFlag = false;
        int tryCrawlCnt = 0;                //失败后尝试重试

        while (!crawlFlag) {
            crawlFlag = beginCrawl(dateFrom, dateTo);
            if (tryCrawlCnt++ == 5)
                break;
        }

        /**
         * 如果成功,记录成功日志
         * 否则,记录失败日志
         */
        if (crawlFlag) {
            saveCrawl();
            System.out.println(Calendar.getInstance().getTime().toLocaleString() + "执行成功");
        } else {
            System.out.println(Calendar.getInstance().getTime().toLocaleString() + "执行失败");
        }

        System.out.println(Calendar.getInstance().getTime().toLocaleString() + "结束执行任务");
        System.out.println("====================");
    }

    /**
     * 开始爬取数据
     *
     * @throws IOException
     */
    private static boolean beginCrawl(String dateFrom, String dateTo) {
        System.out.println("====================");
        System.out.println("开始尝试爬取数据...");
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(FORM_URL);
        post.setHeader("Cookie", getCookies());//保存登录后的Cookies状态

        List<NameValuePair> params = packFormParams(dateFrom, dateTo);

        try {
            post.setEntity(new UrlEncodedFormEntity(params));
            System.out.println("连接成功,发送请求成功...");
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage());
            return false;
        }

        System.out.println("开始写入响应的缓存数据...");
        BufferedReader bufReader;
        try {
            bufReader = new BufferedReader(
                    new InputStreamReader(
                            client.execute(post).getEntity().getContent()));
        } catch (IOException e) {
            logger.error(e.getMessage());
            return false;
        }

        writeFile(bufReader);//保存数据到本地文件
        System.out.println("数据写入本地缓存文件成功...");
        System.out.println("====================");
        System.out.println();

        post.releaseConnection();

        return true;
    }

    /**
     * 读取本地缓存文件,匹配数据并写入数据库
     */
    private static boolean saveCrawl() {
        try {
            System.out.println("====================");
            System.out.println("开始读取本地缓存数据,尝试保存到数据库...");

            BufferedInputStream in = new BufferedInputStream(
                    new FileInputStream(new File(FILE_PATH)));
            StringBuilder sb = new StringBuilder();

            while (in.available() > 0) {
                sb.append((char) in.read());
            }

            System.out.println("读取缓存数据成功,正在保存到数据库...");

            String content = sb.toString().replace("&nbsp;", "");
            Matcher matcher = Pattern.compile(REGEX_PATTERN).matcher(content);
            boolean isFind = matcher.find();

            int count = 0;
            Table table = new Table();
            ILucroDao lucroDao = new LucroDaoImpl();

            try {
                while (isFind) {
                    table.setDate(DateKit.Date2Timestamp(DateKit.Str2Date(matcher.group(1))));
                    matcher.find();
                    table.setTransaction(matcher.group(1));
                    matcher.find();
                    table.setCustomer(matcher.group(1));
                    matcher.find();
                    table.setLevel(Integer.valueOf(matcher.group(1)));
                    matcher.find();
                    table.setSymbol(matcher.group(1));
                    matcher.find();
                    table.setVolume(Double.parseDouble(matcher.group(1)));
                    matcher.find();
                    table.setCommission(Double.parseDouble(matcher.group(1)));
                    matcher.find();
                    if (lucroDao.save(table) == 1) {
                        count++;
                    }
                }
            } catch (Exception e) {
                /*
                此处使用异常来跳出最后一个多余而无法匹配的数据
                 */
                System.out.println("匹配结束,插入到数据库成功");
                System.out.println("本次添加条数" + count);
                System.out.println("====================");
                System.out.println();
                return true;
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
            System.out.println("匹配结束,插入到数据库失败");
            System.out.println("====================");
            System.out.println();
            return false;
        }
        System.out.println("匹配结束,插入到数据库失败");
        System.out.println("====================");
        System.out.println();
        return false;
    }

    /**
     * 获取登录后的cookie
     *
     * @return
     * @throws IOException
     */
    private static String getCookies() {
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(LOGIN_URL);
        StringBuilder sb = new StringBuilder();

        List<NameValuePair> params = packLoginParams();//打包请求登录参数
        try {
            post.setEntity(new UrlEncodedFormEntity(params));
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage());
        }

        try {
            client.execute(post);//执行POST请求
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
        List<Cookie> cookies = ((AbstractHttpClient) client).getCookieStore().getCookies();//获取请求后的cookie
        post.releaseConnection();//释放连接

        //封装登录后的Cookie信息
        for (Cookie cookie : cookies) {
            sb.append(cookie.getName() + "=" + cookie.getValue() + ";");
        }

        return sb.toString();
    }

    /**
     * 打包查询数据表单的请求参数
     * datefrom             起始时间
     * dateto               截止时间
     * sub_accounts         账户
     * levels               查询筛选等级
     * selnumrows           查询数据条数
     *
     * @return
     */
    private static List<NameValuePair> packFormParams(String dateFrom, String dateTo) {
        return packParams(
                new BasicNameValuePair("datefrom", dateFrom),
                new BasicNameValuePair("dateto", dateTo),
                new BasicNameValuePair("Sub_Accounts", SUB_ACCOUNTS),
                new BasicNameValuePair("levels", LEVELS),
                new BasicNameValuePair("selnumrows", SELNUMROWS));
    }

    /**
     * 打包登录请求参数
     * amember_login    登录的用户名
     * amember_pass     登录的密码
     *
     * @return
     */
    private static List<NameValuePair> packLoginParams() {
        return packParams(new BasicNameValuePair("amember_login", AMEMBER_LOGIN),
                new BasicNameValuePair("amember_pass", AMEMBER_PASS));
    }

    /**
     * 打包请求参数
     *
     * @param para 可变请求参数数组
     * @return
     */
    private static List<NameValuePair> packParams(BasicNameValuePair... para) {
        List<NameValuePair> params = new ArrayList<>();
        for (int i = 0; i < para.length; i++) {
            params.add(para[i]);
        }
        return params;
    }

    /**
     * 将响应的数据写入本地
     *
     * @param reader 读缓存池
     */
    private static void writeFile(BufferedReader reader) {

        int index = FILE_PATH.lastIndexOf("/");
        String folder = FILE_PATH.substring(0, index);
        File folderFile = new File(folder);

        if (!folderFile.exists()) {
            folderFile.mkdirs();
        }

        File file = new File(FILE_PATH);
        try {
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();
        } catch (IOException var1) {
            logger.error(var1.getMessage());
        }


        String line;
        BufferedWriter writer = null;

        try {
            writer = new BufferedWriter(new FileWriter(file, true));//以可追加的形式写入文件

            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.flush();
            }
        } catch (IOException var2) {
            logger.error(var2.getMessage());
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException var3) {
                    logger.error(var3.getMessage());
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException var4) {
                    logger.error(var4.getMessage());
                }
            }
        }
    }


}
